﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using agvcontroller.Common;
using agvcontroller.Log;

namespace agvcsimulator.Driver
{
    public delegate void deleSocketSimOnConnect(object sender , enumConnect connect);
    public delegate void deleOnRcvMsg(object sender, string msg);
    public delegate void deleOnSendMsg(object sender, string msg);
    public delegate void deleOnRcvVehicleJob(object sender , CVehicleJob vehicleJob);

    public class CVehicleSimSocketDriver
    {
        public event deleSocketSimOnConnect onConnect;
        public event deleOnRcvMsg onRcvMsg;
        public event deleOnSendMsg onSendMsg;
        public event deleOnRcvVehicleJob onRcvVehicleJob;

        Logger Log;

        Thread thrdClient;
        Socket Client;
        IPAddress ServerIP;
        IPEndPoint ServerEndPoint;
        int Port;
        public string Name;

        public bool Connected;

        public CVehicleSimSocketDriver(IPAddress serverip, int port)
        {
            Name = string.Format("SimSocketDirver_{0}", port);
            Log = new Logger(Name);
            ServerIP = serverip;
            Port = port;

            ServerEndPoint = new IPEndPoint(ServerIP, Port);
            Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            thrdClient = new Thread(new ThreadStart(ClientProc));

            Log.Info(string.Format("Create Socket Driver (Server IP : {0} , Port : {1})", ServerIP.ToString(), Port));
        }

        public int Connect()
        {
            try
            {
                if (Client.Connected) return 0;

                Client.Connect(ServerIP, Port);

                if (onConnect!= null)
                    onConnect(this , enumConnect.CONNECTION);

                thrdClient.Start();

                Connected = true;

                if (onConnect != null)
                    onConnect(this, enumConnect.CONNECTION);

                Log.Info(string.Format("Connect Socket(Server IP : {0} , Port : {1})", ServerIP.ToString(),Port));
            }
            catch (SocketException ex)
            {
                Log.Info(string.Format("{0} , {1})", ex.Message , ex.StackTrace));
                return -1;
            }

            return CReturnCode.SUCCESS;
        }

        public int Close()
        {
            thrdClient.Abort();
            
            Client.Close();

            if (onConnect != null)
                onConnect(this, enumConnect.DISCONNECTION);

            Connected = false;
            if (onConnect != null)
                onConnect(this, enumConnect.DISCONNECTION);
            
            Log.Info(string.Format("Closed Socket({0})", Port));

            return CReturnCode.SUCCESS;
        }

        public void Send(string msg)
        {
            if (Client.Connected)
                Client.Send(Encoding.ASCII.GetBytes(msg));

            if (onSendMsg != null)
                onSendMsg(this, msg);

            Log.Info(string.Format("Send Message({0}) : {1}",Port,msg));
        }

        public void ClientProc()
        {
            while(true)
            {
                byte[] buffer = new byte[1024];

                int length = Client.Receive(buffer);

                if (length == 0)
                {
                    Connected = false;
                }
                else
                {
                    string msg = Encoding.ASCII.GetString(buffer, 0, buffer.Length);

                    PaserMessage(msg);

                    if (onRcvMsg != null)
                    {
                        onRcvMsg(this, msg);

                        Log.Info(string.Format("Receive Message : {0}",msg));
                    }
                }

                Thread.Sleep(100);
            }
        }

        public void PaserMessage(string msg)
        {
            string[] strmsg = msg.Split(':');

            switch (strmsg[0])
            {
                case "REQUEST_VEHICLE_JOB":
                    CVehicleJob vehicleJob = GetVehicleJob(strmsg[1]);

                    if (onRcvVehicleJob != null)
                        onRcvVehicleJob(this, vehicleJob);

                    Send(string.Format("ReceiveMessage:{0}", strmsg[0]));
                    break;
                case "REQUEST_STOP":
                    Send(string.Format("ReceiveMessage:{0}", strmsg[0]));
                    break;
                case "RELEASE_STOP":
                    Send(string.Format("ReceiveMessage:{0}", strmsg[0]));
                    break;
                case "REQUEST_VEHICLE_INFO":
                    Send(string.Format("ReceiveMessage:{0}", strmsg[0]));
                    break;
                case "REQUEST_POSITION":
                    break;
                default:
                    Send(string.Format("Invalid Message : {0}", msg));
                    break;
            }
        }

        CVehicleJob GetVehicleJob(string msg)
        {
            CVehicleJob vehicleJob = new CVehicleJob();
            string[] strParam = msg.Split(',');

            vehicleJob.ID = Convert.ToString(strParam[0]);
            vehicleJob.Route = GetRoute(strParam[1]);
            vehicleJob.Speed= GetSpeed(strParam[2]);
            vehicleJob.Direct = GetDirect(strParam[3]);
            vehicleJob.Action = GetAction(strParam[4]);

            return vehicleJob;
        }

        List<CNode> GetRoute(string msg)
        {
            List<CNode> routeList = new List<CNode>();
            string[] strParam = msg.Split('/');

            foreach(string route in strParam)
            {
                CNode node = CNodeDic.getInstance()[Convert.ToInt32(route)];
                routeList.Add(node);
            }

            return routeList;
        }

        List<int> GetSpeed(string msg)
        {
            List<int> speedList = new List<int>();
            string[] strParam = msg.Split('/');

            foreach (string speed in strParam)
            {
                speedList.Add(Convert.ToInt32(speed));
            }

            return speedList;
        }

        List<enumVehicleDirect> GetDirect(string msg)
        {
            List<enumVehicleDirect> directList = new List<enumVehicleDirect>();
            string[] strParam = msg.Split('/');

            foreach (string direct in strParam)
            {
                directList.Add(enumVehicleDirectString.ConvertEnum((Convert.ToString(direct))));
            }

            return directList;
        }

        List<enumAction> GetAction(string msg)
        {
            List<enumAction> actionList = new List<enumAction>();
            string[] strParam = msg.Split('/');

            foreach (string action in strParam)
            {
                actionList.Add(enumActionString.ConvertEnum(Convert.ToString(action)));
            }

            return actionList;
        }


        public void ReportCommnucation(enumCommnication value)
        {
            switch(value)
            {
                case enumCommnication.ONLINE:
                    Send("Communication:ONLINE");
                    break;
                case enumCommnication.OFFLINE:
                    Send("Communication:OFFLINE");
                    break;
            }
        }

        public void ReportOperation(enumOperation value)
        {
            switch (value)
            {
                case enumOperation.AUTO:
                    Send("Operation:AUTO");
                    break;
                case enumOperation.MANUAL:
                    Send("Operation:MANUAL");
                    break;
            }
        }

        public void ReportControlState(enumContolState value)
        {
            switch (value)
            {
                case enumContolState.RUN:
                    Send("Control:RUN");
                    break;
                case enumContolState.IDLE:
                    Send("Control:IDLE");
                    break;
                case enumContolState.DOWN:
                    Send("Control:DOWN");
                    break;
                case enumContolState.CHARGER:
                    Send("Control:CHARGER");
                    break;
            }
        }

        public void ReportPosition(int preNodeID, int curNodeID, int nextNodeId)
        {
            Send(string.Format("Position:{0},{1},{2}",preNodeID.ToString(),curNodeID.ToString(),nextNodeId.ToString()));
        }

        public void ReportAlarm(int alarmId, bool set)
        {
            Send(string.Format("Alarm:{0},{1}",alarmId.ToString() , set==true?"Set":"Release"));
        }

        public void ReportVehicleInfo(stVehicleInfo info)
        {
            Send(string.Format("VehicleInfo:{0},{1},{2},{3} ",info.Power,info.MotorStart, info.EStop , info.Break));
        }

        public void AcceptVehicleJob(int vehicleJobId)
        {
            Send(string.Format("AcceptVehicleJob:{0}", vehicleJobId));
        }

        public void RejectVehicleJob(int vehicleJobId)
        {
            Send(string.Format("RejectVehicleJob:{0}", vehicleJobId));
        }

        public void CompletedVehicleJob(int vehicleJobId)
        {
            Send(string.Format("CompletedVehicleJob:{0}", vehicleJobId));
        }
    }
}
